package xyz.scootaloo.mono.security.realm

import org.apache.shiro.authc.*
import org.apache.shiro.authz.AuthorizationInfo
import org.apache.shiro.authz.SimpleAuthorizationInfo
import org.apache.shiro.realm.AuthorizingRealm
import org.apache.shiro.subject.PrincipalCollection
import org.apache.shiro.util.ByteSource
import org.springframework.beans.factory.annotation.Autowired
import org.springframework.stereotype.Component
import xyz.scootaloo.mono.base.util.EncryptUtil
import xyz.scootaloo.mono.data.cache.RoleCacheManager
import xyz.scootaloo.mono.data.service.AuthService
import xyz.scootaloo.mono.security.matcher.DoNotCheckMatcher
import xyz.scootaloo.mono.security.token.ClientID

/**
 * 根据用户名和密码, 检查是否是有效用户, 并赋予用户权限
 *
 * @author flutterdash@qq.com
 * @since 2021/7/26 14:04
 */
@Component("passwordRealm")
class PasswordRealm : AuthorizingRealm() {

    @Autowired lateinit var authService: AuthService

    init {
        credentialsMatcher = DoNotCheckMatcher
    }

    override fun supports(token: AuthenticationToken?): Boolean =
        token is UsernamePasswordToken

    /**
     * 验证
     */
    override fun doGetAuthenticationInfo(token: AuthenticationToken): AuthenticationInfo {
        val passwordToken = token as UsernamePasswordToken
        val authUser = authService.findAuthUserByName(passwordToken.username) ?:
            throw AuthenticationException("用户名或者密码错误")
        if (!EncryptUtil.match(authUser.password, String(passwordToken.password), authUser.salt))
            throw AuthenticationException("用户名或者密码错误")
        val client = ClientID.of(authUser)
        return SimpleAuthenticationInfo(
            client, authUser.password, ByteSource.Util.bytes(ENCRYPT_SALT), "passwordRealm"
        )
    }

    /**
     * 授权
     */
    override fun doGetAuthorizationInfo(principals: PrincipalCollection): AuthorizationInfo {
        val client = principals.primaryPrincipal as ClientID
        return SimpleAuthorizationInfo().apply {
            addRoles(RoleCacheManager.list(client.roleCode))
        }
    }

    companion object Encrypt {
        const val ENCRYPT_SALT = "flutterdash@qq.com"
    }
}
